﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using XDL.Framework;
using XDL.Framework.Particles;
using XDL.Framework.Weapons;

namespace JumpNFight.Screens
{
    class RenderLayer : 
        DrawableGameComponent
    {
        ContentManager m_content;
        SpriteBatch spriteBatch;       
        SpriteFont m_debugFont;
        SpriteFont m_messageFont;
        SpriteFont m_crateScoreFont;
        Texture2D m_blankTexture;
        Texture2D[] m_playerTextures = new Texture2D[Simulation.World.MaxPlayers];
        Texture2D[] m_idlePlayerTextures = new Texture2D[Simulation.World.MaxPlayers];
        Texture2D[] m_badGuyTextures = new Texture2D[3];
        Texture2D m_crateTexture;
        Simulation.World m_world;
        Timer m_fireTimer = new Timer(TimeSpan.FromSeconds(0.12));
        ParticleSystem m_baseFire;
        Dictionary<string, Texture2D> m_weaponImageByName = new Dictionary<string, Texture2D>();
        // sub-system renderer
        List<BalisticWSRendererBase> m_weaponRenderers = new List<BalisticWSRendererBase>(8);

        static Random g_rnd = new Random();

        ContentManager Content
        {
            get
            {
                return m_content;
            }
        }        

        public RenderLayer(Game game) :
            base(game)
        {
            m_content = new ContentManager(game.Services);
            m_content.RootDirectory = "Content";

            m_world = GameServices.GetService<Simulation.World>();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(Game.GraphicsDevice);

            // load game content
            m_playerTextures[0] = this.Content.Load<Texture2D>("RedHair");
            m_idlePlayerTextures[0] = this.Content.Load<Texture2D>("RedHairIdle");
            m_playerTextures[1] = this.Content.Load<Texture2D>("YellowHair");
            m_idlePlayerTextures[1] = this.Content.Load<Texture2D>("YellowHairIdle");
            m_badGuyTextures[0] = this.Content.Load<Texture2D>("GreenGundam");
            m_badGuyTextures[1] = this.Content.Load<Texture2D>("RedGundam");
            m_badGuyTextures[2] = this.Content.Load<Texture2D>("FlyingRobot");
            m_blankTexture = this.Content.Load<Texture2D>("Blank");
            m_debugFont = this.Content.Load<SpriteFont>("Debug");
            m_messageFont = this.Content.Load<SpriteFont>("Fonts/Message");
            m_crateScoreFont = this.Content.Load<SpriteFont>("Fonts/CrateScore");
            //m_backgroundTexture = this.Content.Load<Texture2D>("Level1");
            m_crateTexture = this.Content.Load<Texture2D>("Crate");
            m_baseFire = GameServices.GetService<ParticleSystem>("Particles/FireBase");

            // load weapon images
            foreach (IWeapon it in m_world.Weapons)
            {
                string wpName = it.Settings.Name;
                m_weaponImageByName.Add(wpName,this.Content.Load<Texture2D>(wpName));
            }

            BalisticWSRendererBase ws = new GrenadeLauncherWSRenderer(Game);
            RegisterWeaponRenderer(ws);
            ws = new DiscGunWSRenderer(Game);
            RegisterWeaponRenderer(ws);
            ws = new LandMineWSRenderer(Game);
            RegisterWeaponRenderer(ws);

            base.LoadContent();
        }

        void RegisterWeaponRenderer(BalisticWSRendererBase wsRenderer)
        {
            wsRenderer.SpriteBatch = spriteBatch;
            wsRenderer.Content = Content;
            wsRenderer.Initialize();
            m_weaponRenderers.Add(wsRenderer);
        }

        protected override void UnloadContent()
        {
            base.UnloadContent();
            m_content.Unload();
        }

        public override void Update(GameTime gameTime)
        {
            m_fireTimer.Update(gameTime);

            if (m_fireTimer.IsSignaled)
            {
                //
                foreach (Vector2i fireTile in m_world.Level.FireTiles)
                {
                    int n = 16; //  g_rnd.Next(10, 25);
                    for (int i = 0; i < n; i++)
                    {
                        float x = MathHelper.Lerp(fireTile.X * Simulation.World.UnitToPixel, (fireTile.X + 1) * Simulation.World.UnitToPixel, (float)g_rnd.NextDouble());
                        m_baseFire.AddParticle(new Vector3(x, (fireTile.Y + 1) * Simulation.World.UnitToPixel - 5, 0), new Vector3(0, 0, 0));
                    }
                }
                m_fireTimer.Reset();
            }

            foreach(BalisticWSRendererBase it in m_weaponRenderers)
                it.Update(gameTime);

            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.Default, RasterizerState.CullNone);
            // draw world & tiles!
            spriteBatch.Draw(m_world.Map.Background, new Rectangle(0,0,m_world.Width * Simulation.World.UnitToPixel,m_world.Height * Simulation.World.UnitToPixel), Color.White);

            // draw crate
            spriteBatch.Draw(m_crateTexture, new Rectangle(m_world.Crate.Tile.X * Simulation.World.UnitToPixel, m_world.Crate.Tile.Y * Simulation.World.UnitToPixel, 32,32), Color.White);

            // draw badguys            
            foreach (Simulation.NPCBase it in m_world.NPCs)
            {
                if (GameServices.DebugDraw)
                {
                    Simulation.FlyingBadGuy flying = it as Simulation.FlyingBadGuy;
                    if (flying != null && flying.Path != null)
                    {
                        foreach (Vector2i p in flying.Path)
                        {
                            spriteBatch.Draw(m_blankTexture, new Rectangle(p.X * Simulation.World.UnitToPixel, p.Y * Simulation.World.UnitToPixel, 32, 32), Color.DeepPink);
                        }
                    }
                }

                int w = it.Moveable.Width;
                int h = it.Moveable.Height;

                Vector2 offset = new Vector2(w/4, h/4);
                Vector2 pos = new Vector2(it.Moveable.X, it.Moveable.Y);
                if (GameServices.DebugDraw)
                    spriteBatch.Draw(m_blankTexture, new Rectangle(it.Moveable.TileX * Simulation.World.UnitToPixel, it.Moveable.TileY * Simulation.World.UnitToPixel, w, h), Color.DarkRed);

                int frame = 0;
                if ( it.IsAlive)
                    frame = ((int)(25 * gameTime.TotalGameTime.TotalSeconds + it.ID)) % 6;

                float rotation = 0;
                if (!it.IsAlive)
                    rotation = (float)(gameTime.TotalGameTime.TotalSeconds - it.LastHitTime.TotalSeconds);

                Color baseColor = Color.White;
                if ((gameTime.TotalGameTime.TotalSeconds - it.LastHitTime.TotalSeconds) < 0.15)
                {
                    baseColor = Color.Red;
                }

                // 
                Texture2D badGuyTexture = m_badGuyTextures[it.NPCType];
                // 
                if (it.IsSliced)
                {
                    rotation *= 0.01f;
                    offset = new Vector2(w/4, h/4);
                    int sliceHeight = it.Sliceable.SliceHeight;
                    spriteBatch.Draw(badGuyTexture, new Rectangle((int)pos.X, (int)pos.Y - 2 * 4, w, 2 * badGuyTexture.Height - sliceHeight), new Rectangle(0, 0, w / 2, badGuyTexture.Height - sliceHeight / 2), baseColor, rotation, offset, it.IsRunningRight ? SpriteEffects.None : SpriteEffects.FlipHorizontally, 0);
                    offset = new Vector2(w / 4, -badGuyTexture.Height / 2 + sliceHeight / 2);
                    // offset = new Vector2(w / 4, badGuyTexture.Height - it.SliceHeight / 2);
                    spriteBatch.Draw(badGuyTexture, new Rectangle((int)it.Sliceable.X, (int)it.Sliceable.Y - sliceHeight / 2, w, sliceHeight), new Rectangle(0, badGuyTexture.Height - sliceHeight / 2, w / 2, sliceHeight / 2), baseColor, -rotation, offset, it.IsRunningRight ? SpriteEffects.None : SpriteEffects.FlipHorizontally, 0);
                }
                else
                {
                    // standard 
                    spriteBatch.Draw(badGuyTexture, new Rectangle((int)pos.X, (int)pos.Y - 2 * 4, w, 2 * badGuyTexture.Height), new Rectangle(frame * w / 2, 0, w/2, badGuyTexture.Height), baseColor, rotation, offset, it.IsRunningRight ? SpriteEffects.None : SpriteEffects.FlipHorizontally, 0);
                }

                if (GameServices.DebugDraw)
                {
                    spriteBatch.DrawString(m_debugFont, string.Format("{0:#0.00}/{1:#0.00}\nGround:{2}\nHealth:{3}", it.Moveable.Velocity.X, it.Moveable.Velocity.Y, it.Moveable.TouchingGround, it.Health), pos + new Vector2(it.Moveable.Width, 0), Color.DarkRed);
                }
            }

            // draw players
            foreach (Simulation.Player it in m_world.Players)
            {
                if (it.IsActive)
                {
                    Vector2 pos = new Vector2(it.Position.X - it.Width / 2, it.Position.Y - it.Height / 2);
                    if ( GameServices.DebugDraw )
                        spriteBatch.Draw(m_blankTexture, new Rectangle(it.Moveable.TileX * Simulation.World.UnitToPixel, it.Moveable.TileY * Simulation.World.UnitToPixel, 32, 32), Color.Green);

                    int frame = 0;
                    if (Math.Abs(it.Moveable.VX) > 3f)
                    {
                        // ouch: todo, use delta time!!!
                        // make sure the frame covers the *whole* animation (e.g. from rest position to rest position - needed to sync up weapon animation)
                        frame = ((int)(18 * gameTime.TotalGameTime.TotalSeconds + it.ID)) % 7;
                    }

                    float rotation = 0;
                    if (!it.IsAlive)
                        rotation = (float)(gameTime.TotalGameTime.TotalSeconds - it.LastHitTime.TotalSeconds);

                    // 
                    int weaponXOffset = 0;
                    Texture2D texture = null;
                    if (it.Moveable.Velocity.LengthSquared() <= 0.5)
                    {
                        texture = m_idlePlayerTextures[(int)it.ID];
                        frame = ((int)(10 * gameTime.TotalGameTime.TotalSeconds + it.ID)) % 7;
                    }
                    else
                    {
                        texture = m_playerTextures[(int)it.ID];
                        // make sure the weapon is following the player gait
                        if (it.IsAlive)
                            weaponXOffset = (frame < 4 ? frame : 7 - frame);
                    }

                    if (it.IsSliced)
                    {
                        int w = it.Moveable.Width;
                        int h = it.Moveable.Height;
                        rotation *= 0.01f;
                        Vector2 offset = new Vector2(w / 4, h / 4);
                        int sliceHeight = it.Sliceable.SliceHeight;
                        spriteBatch.Draw(texture, new Rectangle((int)pos.X, (int)pos.Y, w, 2 * texture.Height - sliceHeight), new Rectangle(0, 0, w / 2, texture.Height - sliceHeight / 2), Color.White, rotation, offset, it.IsRunningRight ? SpriteEffects.None : SpriteEffects.FlipHorizontally, 0);
                        offset = new Vector2(w / 4, -texture.Height / 2 + sliceHeight / 2);
                        // offset = new Vector2(w / 4, badGuyTexture.Height - it.SliceHeight / 2);
                        spriteBatch.Draw(texture, new Rectangle((int)it.Sliceable.X, (int)it.Sliceable.Y - sliceHeight / 2, w, sliceHeight), new Rectangle(0, texture.Height - sliceHeight / 2, w / 2, sliceHeight / 2), Color.White, -rotation, offset, it.IsRunningRight ? SpriteEffects.None : SpriteEffects.FlipHorizontally, 0);
                    }
                    else
                    {
                        // standard 
                        spriteBatch.Draw(texture, new Rectangle((int)pos.X, (int)pos.Y, 32, 32), new Rectangle((frame % 6) * 16, 0, 16, 16), Color.White, 0, Vector2.Zero, it.IsRunningRight ? SpriteEffects.None : SpriteEffects.FlipHorizontally, 0);
                    }
                    
                    // draw weapon
                    if (it.Weapon != null)
                    {
                        int yoffset = 0;
                        if (it.Weapon.Settings.Name == "LightSaber")
                        {
                            yoffset = 8;
                            weaponXOffset += 6;
                        }
                        Texture2D weaponTexture = m_weaponImageByName[it.Weapon.Settings.Name];
                        int weaponAnimationFrame = 0;
                        if (it.Weapon.Settings.FireAnimationDuration != TimeSpan.Zero)
                        {
                            double normalizedAnimationFrame = (gameTime.TotalGameTime.TotalSeconds - it.LastFireTime.TotalSeconds) / it.Weapon.Settings.FireAnimationDuration.TotalSeconds;
                            if (normalizedAnimationFrame >= 0 && normalizedAnimationFrame <= 1)
                            {
                                int frameCount = weaponTexture.Width / 16;
                                weaponAnimationFrame = (int)(normalizedAnimationFrame * frameCount);
                            }
                        }
                        spriteBatch.Draw(weaponTexture, new Rectangle((int)pos.X + (it.IsRunningRight ? it.Width / 2 + 2 * weaponXOffset : -it.Width / 2 - 2 * weaponXOffset), (int)pos.Y - yoffset, 32, 32), new Rectangle(weaponAnimationFrame * 16, 0, 16, 16), Color.White, 0, Vector2.Zero, it.IsRunningRight ? SpriteEffects.None : SpriteEffects.FlipHorizontally, 0);
                    }

                    if ( GameServices.DebugDraw )
                        spriteBatch.DrawString(m_debugFont, string.Format("{0:#0.00}/{1:#0.00}\nGround:{2}", it.Position.X, it.Position.Y, it.Moveable.TouchingGround), pos + new Vector2(it.Width, 0), Color.White);
                }
            }

            // sub-systems
            foreach(BalisticWSRendererBase it in m_weaponRenderers)
                it.Draw(gameTime);

            spriteBatch.Draw(m_world.Map.Foreground, new Rectangle(0, 0, m_world.Width * Simulation.World.UnitToPixel, m_world.Height * Simulation.World.UnitToPixel), Color.White);

            if (GameServices.DebugDraw)
            {
                float dbgY = 0;
                spriteBatch.DrawString(m_debugFont, string.Format("Players: {0} {1}", m_world.IsActive(PlayerIndex.One), m_world.IsActive(PlayerIndex.Two)), new Vector2(8, 8 + dbgY), Color.White);
                dbgY += m_debugFont.LineSpacing;
                spriteBatch.DrawString(m_debugFont, string.Format("Bad guys#: {0} ", m_world.NPCs.Count), new Vector2(8, 8 + dbgY), Color.White);
                dbgY += m_debugFont.LineSpacing;
                spriteBatch.DrawString(m_debugFont, string.Format("Listeners#: {0} ", GameServices.EventDispatcher.ActiveClients), new Vector2(8, 8 + dbgY), Color.White);
            }

            // crate messages
            foreach (Simulation.CrateMessage it in m_world.Messages)
            {
                Vector2 messageSize = m_messageFont.MeasureString(it.Message) * 2;
                spriteBatch.DrawString(m_messageFont, it.Message, new Vector2(it.Position.X - messageSize.X / 2 + 2, it.Position.Y + 2), Color.Black,0,Vector2.Zero,2,SpriteEffects.None,0);
                spriteBatch.DrawString(m_messageFont, it.Message, new Vector2(it.Position.X - messageSize.X / 2, it.Position.Y), Color.White,0,Vector2.Zero,2,SpriteEffects.None,0);
            }

            string crateScore = m_world.CrateScore.ToString();
            Vector2 crateScoreSize = 12 * m_crateScoreFont.MeasureString(crateScore);
            spriteBatch.DrawString(m_crateScoreFont, crateScore, new Vector2(12 * Simulation.World.UnitToPixel - crateScoreSize.X / 2, -0.5f * Simulation.World.UnitToPixel + 4 - 16), Color.Black, 0, Vector2.Zero, 12, SpriteEffects.None, 0);
            spriteBatch.DrawString(m_crateScoreFont, crateScore, new Vector2(12 * Simulation.World.UnitToPixel - crateScoreSize.X / 2, -0.5f * Simulation.World.UnitToPixel - 16), Color.White, 0, Vector2.Zero, 12, SpriteEffects.None, 0);

            spriteBatch.End();
        }

        public void Stop()
        {
          // nothing to do for now
        }
    }
}
